﻿#region Legal

// Jimmy Zimmerman
// Team Mongoose
//
// END USER LICENSE AGREEMENT
// IMPORTANT - READ THIS CAREFULLY:  This End User License Agreement is a legal agreement
// between you (either an individual, business entity, currently active identity of an
// individual with Multiple Personality Disorder, octopus overlord, or otherworldly entity),
// and Team Mongoose, for the enclosed, components.
//
// By reading this document and/or installing this product, you agree to be bound by the
// terms of this EULA.
//
// Team Mongoose owns all copyright, trade secret, trademark, trade wars,
// patent, portent, and potential rights to these components.  Team Mongoose
// grants you the right to deploy the enclosed components.
//
// If you agree to the terms of this EULA, a license to use these components is granted to you.
//
// If you should happen to benefit from the enclosed components, then you are legally and
// contractually bound to thank us for it. Send your regards to jimmyzimms@hotmail.com.
//
// OTHER RIGHTS AND LIMITATONS
// You may not reverse-engineer, decompile, decode, disassemble, psycho-analyze, or otherwise
// attempt to find hidden meanings between the lines of this EULA; unless, of course, you should
// happen to find some, and they are funny.
//
// You may not translate this EULA or any part of the components into Ancient Sumerian.
//
// THESE COMPONENTS ARE PROVIDED “AS-IS” WITHOUT WARRANTY OF ANY KIND. ANY USE OF THE COMPONENTS
// CONTAINED IS AT YOUR OWN RISK. TEAM MONGOOSE DISCLAIM ALL WARRANTIES, EITHER
// EXPRESS OR IMPLIED, WITH RESPECT TO THE ACCURRACY AND CORRECTNESS OF THE COMPONENTS CONTAINED
// HEREIN. TEAM MONGOOSE DOES NOT WARRANT THAT THE COMPONENTS ARE FLAWLESS.
//
// REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT MODIFICATION, ARE PERMITTED
// PROVIDED THAT THE FOLLOWING CONDITIONS ARE MET:
// * REDISTRIBUTIONS OF SOURCE CODE MUST RETAIN THE ABOVE COPYRIGHT NOTICE
// * REDISTRIBUTIONS IN BINARY FORM MUST NOTE THE USE OF THE COMPONENT IN DOCUMENTATION AND/OR
//   OTHER MATERIALS PROVIDED WITH THE DISTRIBUTION.
// * NEITHER THE NAME OF TEAM MONGOOSE MAY BE USED TO ENDORES OR PROMOTE PRODUCTS
//   DERIVED FROM THIS SOFTWARE WITHOUT SPECIFIC PRIOR WRITTEN PERMISSION.
//
// IN NO EVENT SHALL TEAM MONGOOSE BE HELD LIABLE FOR INCIDENTAL, SPECIAL, INDIRECT,
// INCONSEQUENTIAL, UNBELIEVABLE, EXAGGERATED, VERBOSE, OR TYPICAL DAMAGES INCURRED WHILE USING
// THE ENCLOSED COMPONENTS.
//
// OUR STUFF ALWAYS WORKS - SOMETIMES.

#endregion

using Microsoft.AnalysisServices.AdomdClient;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Common;

namespace System.Data.MdxClient
{
    /// <summary>
    /// Represents a MDX parameter collection.
    /// </summary>
    public class MdxParameterCollection : DbParameterCollection
    {
        #region Fields

        private readonly ParamaterCollection items = new ParamaterCollection();
        private readonly AdomdCommand command;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the internal, native ADOMD.NET command associated with the parameters.
        /// </summary>
        /// <value>An <see cref="AdomdCommand"/> object.</value>
        protected internal AdomdCommand AdomdCommand
        {
            get
            {
                return this.command;
            }
        }

        /// <summary>
        /// Gets the list of items in the collection.
        /// </summary>
        /// <value>An <see cref="IList{T}"/> object.</value>
        protected virtual IList<MdxParameter> Items
        {
            get
            {
                return this.items;
            }
        }

        /// <summary>
        /// Gets a dictionary that maintains the name-to-parameter mapping.
        /// </summary>
        /// <value>An <see cref="IDictionary{TKey,TValue}"/> object.</value>
        protected virtual IDictionary<string, MdxParameter> Dictionary
        {
            get
            {
                return this.items.KeyDictionary;
            }
        }

        #endregion

        #region Overrides

        /// <summary>
        /// Adds a new parameter to the collection.
        /// </summary>
        /// <param name="value">A value of type <see cref="MdxParameter"/>.</param>
        /// <returns>The zero-based index where the insertion took place.s</returns>
        public override int Add( object value )
        {
            var parameter = (MdxParameter) value;
            this.Items.Add( parameter );

            return this.Items.Count - 1;
        }

        /// <summary>
        /// Adds an array of items with the specified values.
        /// </summary>
        /// <param name="values">An of <see cref="Array"/> of values of type <see cref="MdxParameter"/>
        /// to add to the collection.</param>
        public override void AddRange( Array values )
        {
            foreach ( object value in values )
                this.Add( value );
        }

        /// <summary>
        /// Removes all items from the collection.
        /// </summary>
        public override void Clear()
        {
            this.Items.Clear();
        }

        /// <summary>
        /// Returns a value indicating whether the collection contains the specified value.
        /// </summary>
        /// <param name="value">A value of type <see cref="MdxParameter"/> to evaluate.</param>
        /// <returns>True if the collection contains the value; otherwise, false.</returns>
        public override bool Contains( object value )
        {
            if ( !( value is MdxParameter ) )
                return false;

            return this.Items.Contains( (MdxParameter) value );
        }

        /// <summary>
        /// Returns a value indicating whether the collection contains the specified key.
        /// </summary>
        /// <param name="value">The value of the parameter name to evaluate.</param>
        /// <returns>True if the collection contains the value; otherwise, false.</returns>
        public override bool Contains( string value )
        {
            return this.Dictionary.ContainsKey( value );
        }

        /// <summary>
        /// Copies the contents of the collection to the specified array.
        /// </summary>
        /// <param name="array">An <see cref="Array"/> with zero-based indexing to copy the collection to.</param>
        /// <param name="index">The zero-based index where copying begins.</param>
        public override void CopyTo( Array array, int index )
        {
            ( (ICollection) this.Items ).CopyTo( array, index );
        }

        /// <summary>
        /// Gets the number of items in the collection.
        /// </summary>
        /// <value>The number of items in the collection.</value>
        public override int Count
        {
            get
            {
                return this.Items.Count;
            }
        }

        /// <summary>
        /// Returns an iterator for the collection.
        /// </summary>
        /// <returns>An <see cref="IEnumerator"/> object.</returns>
        public override IEnumerator GetEnumerator()
        {
            return this.Items.GetEnumerator();
        }

        /// <summary>
        /// Return the parameter at the specified index in the collection.
        /// </summary>
        /// <param name="index">The zero-based index of the parameter to retrieve.</param>
        /// <returns>A <see cref="MdxParameter"/> object.</returns>
        protected override DbParameter GetParameter( int index )
        {
            return this.Items[index];
        }

        /// <summary>
        /// Returns the parameter with the specified name in the collection.
        /// </summary>
        /// <param name="parameterName">The name of the parameter to retrieve.</param>
        /// <returns>A <see cref="MdxParameter"/> object.</returns>
        protected override DbParameter GetParameter( string parameterName )
        {
            if ( this.Dictionary.ContainsKey( parameterName ) )
                return this.Dictionary[parameterName];

            return null;
        }

        /// <summary>
        /// Returns the zero-based index of the specified value in the collection.
        /// </summary>
        /// <param name="value">The <see cref="MdxParameter"/> to evaluate.</param>
        /// <returns>The zero-based index of the item in the collection or -1 if no match is found.</returns>
        public override int IndexOf( object value )
        {
            if ( !( value is MdxParameter ) )
                return -1;

            return this.Items.IndexOf( (MdxParameter) value );
        }

        /// <summary>
        /// Returns the zero-based index of the item with the specified parameter name in the collection.
        /// </summary>
        /// <param name="parameterName">The parameter name of the item to retrieve.</param>
        /// <returns>The zero-based index of the item in the collection or -1 if no match is found.</returns>
        public override int IndexOf( string parameterName )
        {
            if ( !this.Dictionary.ContainsKey( parameterName ) )
                return -1;

            return this.Items.IndexOf( this.Dictionary[parameterName] );
        }

        /// <summary>
        /// Inserts a new item in the collection at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index where the insertion takes place.</param>
        /// <param name="value">The <see cref="MdxParameter"/> to insert.</param>
        public override void Insert( int index, object value )
        {
            this.Items.Insert( index, (MdxParameter) value );
        }

        /// <summary>
        /// Gets a value indicating whether the collection is a fixed size.
        /// </summary>
        /// <value>True if the colelction is a fixed size; otherwise, false.</value>
        public override bool IsFixedSize
        {
            get
            {
                return ( (IList) this.Items ).IsFixedSize;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the collection is read-only.
        /// </summary>
        /// <value>True if the collection is read-only; otherwise, false.</value>
        public override bool IsReadOnly
        {
            get
            {
                return ( (IList) this.Items ).IsReadOnly;
            }
        }

        /// <summary>
        /// Get a value indicating whether the collection is synchronized (thread-safe).
        /// </summary>
        /// <value>True if the collection is synchronized (thread-safe); otherwise, false.</value>
        public override bool IsSynchronized
        {
            get
            {
                return ( (ICollection) this.Items ).IsSynchronized;
            }
        }

        /// <summary>
        /// Removes the specified item from the collection.
        /// </summary>
        /// <param name="value">The <see cref="MdxParameter"/> to remove.</param>
        public override void Remove( object value )
        {
            var item = value as MdxParameter;

            if ( item != null )
                this.Items.Remove( item );
        }

        /// <summary>
        /// Removes the item in the collection at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index of the item to remove.</param>
        public override void RemoveAt( int index )
        {
            this.Items.RemoveAt( index );
        }

        /// <summary>
        /// Removes the item in the collection with the speciifed parameter name.
        /// </summary>
        /// <param name="parameterName">The name of the parameter to remove.</param>
        public override void RemoveAt( string parameterName )
        {
            if ( this.Dictionary.ContainsKey( parameterName ) )
                this.Dictionary.Remove( parameterName );
        }

        /// <summary>
        /// Sets the item in the collection at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index of the item to set.</param>
        /// <param name="value">The <see cref="MdxParameter"/> to set.</param>
        protected override void SetParameter( int index, DbParameter value )
        {
            this.Items[index] = (MdxParameter) value;
        }

        /// <summary>
        /// Sets the item in the collection with the specified parameter name.
        /// </summary>
        /// <param name="parameterName">The parameter name of the item to set.</param>
        /// <param name="value">The <see cref="MdxParameter"/> to set.</param>
        /// <remarks>If an item with the specified parameter item does not exist, the value will be added.</remarks>
        protected override void SetParameter( string parameterName, DbParameter value )
        {
            var index = this.IndexOf( parameterName );
            var parameter = (MdxParameter) value;

            if ( index > -1 && index < this.Count - 1 )
                this.Items[index] = parameter;
            else
                this.Items.Add( parameter );
        }

        /// <summary>
        /// Gets an object that can be used to synchronize access to the collection.
        /// </summary>
        /// <value>A synchronization <see cref="Object"/>.</value>
        public override object SyncRoot
        {
            get
            {
                return ( (ICollection) this.Items ).SyncRoot;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Instantiates a new instance of the <see cref="MdxParameterCollection"/> class.
        /// </summary>
        /// <param name="command">The <see cref="AdomdCommand"/> associated with the parameters.</param>
        /// <exception cref="ArgumentNullException"><paramref name="command"/> is null.</exception>
        protected internal MdxParameterCollection( AdomdCommand command )
        {
            if ( ( this.command = command ) == null )
                throw new ArgumentNullException( "command" );
        }

        #endregion

        #region Nested Types

        private sealed class ParamaterCollection : KeyedCollection<string, MdxParameter>
        {
            #region Properties

            public IDictionary<string, MdxParameter> KeyDictionary
            {
                get
                {
                    return this.Dictionary;
                }
            }

            #endregion

            #region Overrides

            protected override string GetKeyForItem( MdxParameter item )
            {
                return item.ParameterName;
            }

            #endregion
        }

        #endregion
    }
}
